package kiosk;

import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.IOException;
import java.util.List;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.control.Label;
import javafx.scene.control.LabelBuilder;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import queues.NotInitializedException;
import queues.QueuesClient;
import queues.QueuesPreloader;
import queues.UnknownSubjectException;
import server.Counter;
import server.IKioskService;
import server.IKioskServiceGetNewTicketArgumentExceptionFaultFaultMessage;
import server.IKioskServiceGetSubjectInfoArgumentExceptionFaultFaultMessage;
import server.QueueInfo;
import server.QueuesService;
import server.Subject;
import server.SubjectInfo;
import server.Ticket;

/**
 *
 * @author Renato Rosa <2101076@my.ipleiria.pt>
 */
public class Kiosk extends QueuesClient<IKioskService> {

    //the scenes - here in a kind of cache, so we need to read and parse the fxml file only at the beginning
    private Scene kioskScene;
    private Scene subjectInfoScene;

    //controllers
    private KioskWindowController kioskController;
    private SubjectInfoWindowController subjectInfoController;

    public Kiosk() {
        super("kiosk.properties");
        notificationsService = new KioskNotifService();

        try {
            loadKioskScene();
            loadSubjectInfoScene();
        } catch (IOException ex) {
            notifyPreloader(new QueuesPreloader.RegistrationNotification(false, "Erro no carregamento da interface. Contacte o administrador."));
        }
    }

    @Override
    public void start(Stage mainStage) throws Exception {
        super.start(mainStage);
        stage.setScene(kioskScene);
    }
    
    @Override
    public void initialize(List<Subject> subjects, List<Counter> counters, List<QueueInfo> tickets) throws NotInitializedException {
        super.initialize(subjects, counters, tickets);
        kioskController.setSubjects(subjects);
        kioskController.showInfo("");
        initialized = true;
    }

    public void presentKiosk() {
        stage.setScene(kioskScene);
        kioskController.showInfo("");
    }

    public void presentSubjectInfo(final int subjectId) {
        //create a task to run asynchronously
        final Task<SubjectInfo> task = new Task<SubjectInfo>() {
            @Override
            protected SubjectInfo call() throws Exception {
                try {
                    return proxy.getSubjectInfo(subjectId);
                } catch (IKioskServiceGetSubjectInfoArgumentExceptionFaultFaultMessage e) {
                    //oops! we asked for a unknown subject! better ask for a new initialization
                    register();
                    //let's throw the exception, in order to cancel the process
                    throw e;
                }
            }
        };

        task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent t) {
                //everything OK, let's present the screen
                subjectInfoController.setSubjectInfo(task.getValue());
                stage.setScene(subjectInfoScene);
            }
        });

        task.setOnFailed(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent t) {
                //something wrong
                showMessage("Erro", "Ocorreu um erro. Aguarde um pouco e tente de novo");
                System.err.println(task.getException().getMessage());
            }
        });

        //execute the Task in background
        Thread th = new Thread(task);
        th.setDaemon(true);
        th.start();
    }

    private void loadKioskScene() throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/KioskWindow.fxml"));
        final Parent root = (Parent) loader.load();
        kioskController = loader.getController();
        kioskController.setApp(this);
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                kioskScene = new Scene(root);
            }
        });
    }

    private void loadSubjectInfoScene() throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/SubjectInfoWindow.fxml"));
        final Parent root = (Parent) loader.load();
        subjectInfoController = loader.getController();
        subjectInfoController.setApp(this);
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                subjectInfoScene = new Scene(root);
            }
        });
    }

    public void showMessage(String title, String message) {
        final Stage dialog = new Stage(StageStyle.UTILITY);
        dialog.initModality(Modality.WINDOW_MODAL);
        dialog.initOwner(stage);
        dialog.centerOnScreen();
        dialog.setResizable(false);
        dialog.setTitle(title);
        
        Label label = LabelBuilder.create().text(message).alignment(Pos.CENTER).build();
        Button button = ButtonBuilder.create().text("OK").alignment(Pos.CENTER).onAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                //close the dialog.
                dialog.close();
            }
        }).build();
        
        VBox root = VBoxBuilder.create().spacing(10.0).children(label, button).alignment(Pos.CENTER).padding(new Insets(20.0)).build();

        dialog.setScene(new Scene(root));
        dialog.sizeToScene();
        dialog.show();
    }

    public void requestTicket(final int idSubject) {
        //create a task to run asynchronously
        final Task<Ticket> task = new Task<Ticket>() {
            @Override
            protected Ticket call() throws Exception {
                try {
                    return proxy.getNewTicket(idSubject);
                } catch (IKioskServiceGetNewTicketArgumentExceptionFaultFaultMessage e) {
                    //oops! we asked for a unknown subject! better ask for a new initialization
                    register();
                    //let's throw the exception, in order to cancel the process
                    throw e;
                }
            }
        };

        task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent t) {
                printTicket(task.valueProperty().getValue());
            }
        });

        task.setOnFailed(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent t) {
                showMessage("Erro", "Ocorreu um erro. Aguarde um pouco e tente de novo");
                System.err.println(task.getException().getMessage());
            }
        });

        Thread th = new Thread(task);
        th.setDaemon(true);
        th.start();
    }

    private void printTicket(Ticket ticket) {
        //get a PrinterJob from default printer
        PrinterJob pj = PrinterJob.getPrinterJob();
        PageFormat page = pj.defaultPage();
        Paper paper = page.getPaper();

        //set paper margins
        paper.setImageableArea(2.0, 2.0, paper.getWidth() - 4.0, paper.getHeight() - 4.0);
        page.setPaper(paper);

        //book is the only way to achieve margin customization
        Book book = new Book();
        book.append(new PrintableTicket(ticket), page, 1);
        pj.setPageable(book);

        try {
            pj.print();
        } catch (PrinterException ex) {
            showMessage("Erro de impressão", "Ocorreu um erro na impressão. A sua senha tem o nº " + ticket.getText());
            //kioskController.showInfo("Ocorreu um erro na impressão. A sua senha tem o nº " + ticket.getText());
            System.err.println("Couldn't print:" + ex.getMessage());
        }
    }

    @Override
    protected void registerImpl() throws Exception {
        proxy = new QueuesService(serverWsdlLocation).getKiosk();
        proxy.register(clientId);
    }

    @Override
    protected void unregisterImpl() throws Exception {
        proxy.unregister(clientId);
    }

    @Override
    public void updateAll(List<Counter> counters, List<QueueInfo> tickets) throws NotInitializedException, UnknownSubjectException {
        //at the moment, Kiosk has no reaction to this kind of notification, but it's prepared to handle them in the future, as needed
    }

    @Override
    public void updateQueue(QueueInfo info) throws NotInitializedException, UnknownSubjectException {
        //at the moment, Kiosk has no reaction to this kind of notification, but it's prepared to handle them in the future, as needed
    }
}